/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  [2002] - [2007] Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package flex.messaging.cluster;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.jgroups.Address;
import org.jgroups.stack.IpAddress;

/**
 * @exclude
 * ClusterNode is an encapsulation for pairing a physical host and a logical
 * software group, which is in effect a mapping between a physical address used
 * by the cluster infrastruture and a service destination used by the message
 * infrastructure.
 *
 * This class is specific to the <code>JGroupsCluster</code> implementation.
 *
 * @author neville
 */
public class ClusterNode
{
    /**
     * The name of the host for this cluster node.
     */
    private final String host;

    /**
     * Mapping between clustered destinations and the
     * clustered endpoint.
     * key = destination key (String)
     * value = Map of channel-id to endpoint-url mappings.
     */
    private final Map<String,Map<String,String>> destKeyToChannelMap;

    /**
     * Constructor.
     */
    ClusterNode(Address address)
    {
        IpAddress addr = (IpAddress) address;
        host = addr.getIpAddress().getCanonicalHostName();
        destKeyToChannelMap = new HashMap<String,Map<String,String>>();
    }

    /**
     * Returns the name of the host for this cluster node.
     *
     * @return The name of the host.
     */
    String getHost()
    {
        return host;
    }

    /**
     * Returns a map of clustered destination to clustered
     * endpoint mappings.
     *
     * @return Map of clustered destination to clustered
    *  endpoint mappings.
     */
    Map<String,Map<String,String>> getDestKeyToChannelMap()
    {
        return destKeyToChannelMap;
    }

    /**
     * Returns a map of clustered endpoints for the specified
     * clustered destination. If there is not currently a
     * map for the destination, an empty mapping is created
     * and returned.
     *
     * The endpoint map is indexed by channel id.
     * The endpoint map contains endpoint urls.
     *
     * @param The service type of the clustered destination.
     * @param The destination name of the clustered destination.
     * @return Map of clustered endpoints.
     */
    Map<String,String> getEndpoints(String serviceType, String destName)
    {
        String destKey = serviceType + ":" + destName;
        synchronized (destKeyToChannelMap)
        {
            Map<String,String> channelEndpoints = destKeyToChannelMap.get(destKey);
            if (channelEndpoints == null)
            {
                channelEndpoints = new HashMap<String,String>();
                destKeyToChannelMap.put(destKey, channelEndpoints);
            }
            return channelEndpoints;
        }
    }

    /**
     * This method adds an endpoint to the list of endpoints for the clustered
     * destination, identified by service type and destination name.
     *
     * @param serviceType The service type of the clustered destination.
     * @param destName The destination name of the clustered destination.
     * @param channelId The channel id to be added to the channel endpoint mapping.
     * @param endpointUrl The endpoint url to be added to the endpoint url mapping.
     */
    void addEndpoint(String serviceType, String destName, String channelId, String endpointUrl)
    {
        synchronized (destKeyToChannelMap)
        {
            Map<String,String> channelEndpoints = getEndpoints(serviceType, destName);
            channelEndpoints.put(channelId, endpointUrl);
        }
    }

    /**
     * Returns whether the endpoint, specified by channel id and endpoint url,
     * is included in the list of endpoints in the clusted destination.
     *
     * @param serviceType The service type of the clustered destination.
     * @param destName The destination name of the clustered destination.
     * @param channelId The channel id to find in the list of endpoints.
     * @param endpointUrl The endpoint url to find in the list of endpoints.
     * @return Whether the endpoint is included in the list for the clustered destination.
     */
    boolean containsEndpoint(String serviceType, String destName, String channelId, String endpointUrl)
    {
        Map<String,String> channelEndpoints = getEndpoints(serviceType, destName);
        return channelEndpoints.containsKey(channelId) && channelEndpoints.get(channelId).equals(endpointUrl);
    }

    /**
     * Returns a description of the clustered node including details
     * on the mapping between the clustered destinations on this node
     * and their endpoint mappings.
     *
     * @return Description of the clustered node.
     */
    public String toString()
    {
        StringBuffer sb = new StringBuffer("ClusterNode[");
        synchronized (destKeyToChannelMap)
        {
            for (Entry<String,Map<String,String>> entry : destKeyToChannelMap.entrySet())
            {
                sb.append(" channels for ");
                sb.append(entry.getKey());
                sb.append('(');
                for (Iterator<Entry<String,String>> iter = entry.getValue().entrySet().iterator(); iter.hasNext();)
                {
                    Entry<String,String> channelMapEntry = iter.next();
                    sb.append(channelMapEntry.getKey());
                    sb.append('=');
                    sb.append(channelMapEntry.getValue());
                    if (iter.hasNext())
                        sb.append(", ");
                }
                sb.append(')');
            }
        }
        sb.append(" ]");
        return sb.toString();
    }
}
